<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT License
 */

require_once dirname(__FILE__) . '/PeerBuilder.php';
require_once dirname(__FILE__) . '/ClassTools.php';

/**
 * Generates a PHP5 base Peer class for user object model (OM).
 *
 * This class produces the base peer class (e.g. BaseMyPeer) which contains all
 * the custom-built query and manipulator methods.
 *
 * @author     Hans Lellelid <hans@xmpl.org>
 * @package    propel.generator.builder.om
 */
class PHP5PeerBuilder extends PeerBuilder
{

    /**
     * Validates the current table to make sure that it won't
     * result in generated code that will not parse.
     *
     * This method may emit warnings for code which may cause problems
     * and will throw exceptions for errors that will definitely cause
     * problems.
     */
    protected function validateModel()
    {
        parent::validateModel();

        $table = $this->getTable();

        // Check to see if any of the column constants are PHP reserved words.
        $colConstants = array();

        foreach ($table->getColumns() as $col) {
            $colConstants[] = $this->getColumnName($col);
        }

        $reservedConstants = array_map('strtoupper', ClassTools::getPhpReservedWords());

        $intersect = array_intersect($reservedConstants, $colConstants);
        if (!empty($intersect)) {
            throw new EngineException("One or more of your column names for [" . $table->getName() . "] table conflict with a PHP reserved word (" . implode(", ", $intersect) . ")");
        }
    }

    /**
     * Returns the name of the current class being built.
     *
     * @return string
     */
    public function getUnprefixedClassname()
    {
        return $this->getBuildProperty('basePrefix') . $this->getStubPeerBuilder()->getUnprefixedClassname();
    }

    /**
     * Gets the package for the [base] peer classes.
     *
     * @return string
     */
    public function getPackage()
    {
        return parent::getPackage() . ".om";
    }

    public function getNamespace()
    {
        if ($namespace = parent::getNamespace()) {
            if ($this->getGeneratorConfig() && $omns = $this->getGeneratorConfig()->getBuildProperty('namespaceOm')) {
                return $namespace . '\\' . $omns;
            } else {
                return $namespace;
            }
        }
    }

    /**
     * Adds the include() statements for files that this class depends on or utilizes.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addIncludes(&$script)
    {
    } // addIncludes()

    /**
     * Adds class phpdoc comment and opening of class.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addClassOpen(&$script)
    {
        $tableName = $this->getTable()->getName();
        $tableDesc = $this->getTable()->getDescription();

        if ($this->getBuildProperty('addClassLevelComment')) {
            $script .= "
/**
 * Base static class for performing query and update operations on the '$tableName' table.
 *
 * $tableDesc
 *";
            if ($this->getBuildProperty('addTimeStamp')) {
                $now = strftime('%c');
                $script .= "
 * This class was autogenerated by Propel " . $this->getBuildProperty('version') . " on:
 *
 * $now
 *";
            }

            $script .= "
 * @package propel.generator." . $this->getPackage() . "
 */";
        }

        $extendingPeerClass = '';
        $parentClass = $this->getBehaviorContent('parentClass');
        if (null !== $parentClass) {
            $extendingPeerClass = ' extends ' . $parentClass;
        }

        $script .= "
abstract class " . $this->getClassname() . $extendingPeerClass . "
{
";
    }

    protected function addClassBody(&$script)
    {
        $this->declareClassFromBuilder($this->getStubPeerBuilder());
        $this->declareClassFromBuilder($this->getStubObjectBuilder());
        parent::addClassBody($script);
        $this->declareClasses('Propel', 'PropelException', 'PropelPDO', 'BasePeer', 'Criteria', 'PDO', 'PDOStatement');
    }

    /**
     * Closes class.
     * Adds closing brace at end of class and the static map builder registration code.
     *
     * @param string &$script The script will be modified in this method.
     *
     * @see        addStaticTableMapRegistration()
     */
    protected function addClassClose(&$script)
    {
        // apply behaviors
        $this->applyBehaviorModifier('staticMethods', $script, "	");

        $script .= "
} // " . $this->getClassname() . "
";
        $this->addStaticTableMapRegistration($script);
    }

    /**
     * Adds the static map builder registration code.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addStaticTableMapRegistration(&$script)
    {
        $table = $this->getTable();

        $script .= "
// This is the static code needed to register the TableMap for this table with the main Propel class.
//
" . $this->getClassName() . "::buildTableMap();

";
        $this->applyBehaviorModifier('peerFilter', $script, "");
    }

    public function getTableMapClass()
    {
        return $this->getStubObjectBuilder()->getClassname() . 'TableMap';
    }

    public function getTablePhpName()
    {
        return ($this->getTable()->isAbstract() ? '' : $this->getStubObjectBuilder()->getClassname());
    }

    /**
     * Adds constant and variable declarations that go at the top of the class.
     *
     * @param string &$script The script will be modified in this method.
     *
     * @see        addColumnNameConstants()
     */
    protected function addConstantsAndAttributes(&$script)
    {
        $dbName = $this->getDatabase()->getName();
        $tableName = $this->getTable()->getName();
        $tablePhpName = $this->getTable()->isAbstract() ? '' : addslashes($this->getStubObjectBuilder()->getFullyQualifiedClassname());
        $script .= "
    /** the default database name for this class */
    const DATABASE_NAME = '$dbName';

    /** the table name for this class */
    const TABLE_NAME = '$tableName';

    /** the related Propel class for this table */
    const OM_CLASS = '$tablePhpName';

    /** the related TableMap class for this table */
    const TM_CLASS = '" . $this->getTableMapClass() . "';

    /** The total number of columns. */
    const NUM_COLUMNS = " . $this->getTable()->getNumColumns() . ";

    /** The number of lazy-loaded columns. */
    const NUM_LAZY_LOAD_COLUMNS = " . $this->getTable()->getNumLazyLoadColumns() . ";

    /** The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS) */
    const NUM_HYDRATE_COLUMNS = " . ($this->getTable()->getNumColumns() - $this->getTable()->getNumLazyLoadColumns()) . ";
";
        $this->addColumnNameConstants($script);
        $this->addInheritanceColumnConstants($script);
        if ($this->getTable()->hasEnumColumns()) {
            $this->addEnumColumnConstants($script);
        }

        $script .= "
    /** The default string format for model objects of the related table **/
    const DEFAULT_STRING_FORMAT = '" . $this->getTable()->getDefaultStringFormat() . "';

    /**
     * An identity map to hold any loaded instances of " . $this->getObjectClassname() . " objects.
     * This must be public so that other peer classes can access this when hydrating from JOIN
     * queries.
     * @var        array " . $this->getObjectClassname() . "[]
     */
    public static \$instances = array();

";

        // apply behaviors
        $this->applyBehaviorModifier('staticConstants', $script, "	");
        $this->applyBehaviorModifier('staticAttributes', $script, "	");

        $this->addFieldNamesAttribute($script);
        $this->addFieldKeysAttribute($script);

        if ($this->getTable()->hasEnumColumns()) {
            $this->addEnumColumnAttributes($script);
        }
    }

    /**
     * Adds the COLUMN_NAME constants to the class definition.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addColumnNameConstants(&$script)
    {
        foreach ($this->getTable()->getColumns() as $col) {
            $script .= "
    /** the column name for the " . $col->getName() . " field */
    const " . $this->getColumnName($col) . " = '" . $this->getTable()->getName() . "." . $col->getName() . "';
";
        } // foreach
    }

    /**
     * Adds the valueSet constants for ENUM columns.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addEnumColumnConstants(&$script)
    {
        foreach ($this->getTable()->getColumns() as $col) {
            if ($col->isEnumType() || $col->getValueSet()) {
                $script .= "
    /** The enumerated values for the " . $col->getName() . " field */";
                foreach ($col->getValueSet() as $value) {
                    $script .= "
    const " . $this->getColumnName($col) . '_' . $this->getEnumValueConstant($value) . " = '" . $value . "';";
                }
                $script .= "
";
            }
        }
    }

    protected function getEnumValueConstant($value)
    {
        return strtoupper(preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '_', $value));
    }

    protected function addFieldNamesAttribute(&$script)
    {
        $table = $this->getTable();

        $tableColumns = $table->getColumns();

        $script .= "
    /**
     * holds an array of fieldnames
     *
     * first dimension keys are the type constants
     * e.g. " . $this->getPeerClassname() . "::\$fieldNames[" . $this->getPeerClassname() . "::TYPE_PHPNAME][0] = 'Id'
     */
    protected static \$fieldNames = array (
        BasePeer::TYPE_PHPNAME => array (";
        foreach ($tableColumns as $col) {
            $script .= "'" . $col->getPhpName() . "', ";
        }
        $script .= "),
        BasePeer::TYPE_STUDLYPHPNAME => array (";
        foreach ($tableColumns as $col) {
            $script .= "'" . $col->getStudlyPhpName() . "', ";
        }
        $script .= "),
        BasePeer::TYPE_COLNAME => array (";
        foreach ($tableColumns as $col) {
            $script .= $this->getColumnConstant($col, $this->getPeerClassname()) . ", ";
        }
        $script .= "),
        BasePeer::TYPE_RAW_COLNAME => array (";
        foreach ($tableColumns as $col) {
            $script .= "'" . $col->getConstantColumnName() . "', ";
        }
        $script .= "),
        BasePeer::TYPE_FIELDNAME => array (";
        foreach ($tableColumns as $col) {
            $script .= "'" . $col->getName() . "', ";
        }
        $script .= "),
        BasePeer::TYPE_NUM => array (";
        foreach ($tableColumns as $num => $col) {
            $script .= "$num, ";
        }
        $script .= ")
    );
";
    }

    protected function addFieldKeysAttribute(&$script)
    {
        $table = $this->getTable();

        $tableColumns = $table->getColumns();

        $script .= "
    /**
     * holds an array of keys for quick access to the fieldnames array
     *
     * first dimension keys are the type constants
     * e.g. " . $this->getPeerClassname() . "::\$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0
     */
    protected static \$fieldKeys = array (
        BasePeer::TYPE_PHPNAME => array (";
        foreach ($tableColumns as $num => $col) {
            $script .= "'" . $col->getPhpName() . "' => $num, ";
        }
        $script .= "),
        BasePeer::TYPE_STUDLYPHPNAME => array (";
        foreach ($tableColumns as $num => $col) {
            $script .= "'" . $col->getStudlyPhpName() . "' => $num, ";
        }
        $script .= "),
        BasePeer::TYPE_COLNAME => array (";
        foreach ($tableColumns as $num => $col) {
            $script .= $this->getColumnConstant($col, $this->getPeerClassname()) . " => $num, ";
        }
        $script .= "),
        BasePeer::TYPE_RAW_COLNAME => array (";
        foreach ($tableColumns as $num => $col) {
            $script .= "'" . $col->getConstantColumnName() . "' => $num, ";
        }
        $script .= "),
        BasePeer::TYPE_FIELDNAME => array (";
        foreach ($tableColumns as $num => $col) {
            $script .= "'" . $col->getName() . "' => $num, ";
        }
        $script .= "),
        BasePeer::TYPE_NUM => array (";
        foreach ($tableColumns as $num => $col) {
            $script .= "$num, ";
        }
        $script .= ")
    );
";
    } // addFieldKeysAttribute

    /**
     * Adds the valueSet attributes for ENUM columns.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addEnumColumnAttributes(&$script)
    {
        $script .= "
    /** The enumerated values for this table */
    protected static \$enumValueSets = array(";
        foreach ($this->getTable()->getColumns() as $col) {
            if ($col->isEnumType() || $col->getValueSet()) {
                $script .= "
        " . $this->getPeerClassname() . "::" . $this->getColumnName($col) . " => array(
";
                foreach ($col->getValueSet() as $value) {
                    $script .= "			" . $this->getStubPeerBuilder()->getClassname() . '::' . $this->getColumnName($col) . '_' . $this->getEnumValueConstant($value) . ",
";
                }
                $script .= "		),";
            }
        }
        $script .= "
    );
";
    }

    protected function addGetFieldNames(&$script)
    {
        $script .= "
    /**
     * Returns an array of field names.
     *
     * @param      string \$type The type of fieldnames to return:
     *                      One of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME
     *                      BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM
     * @return array           A list of field names
     * @throws PropelException - if the type is not valid.
     */
    public static function getFieldNames(\$type = BasePeer::TYPE_PHPNAME)
    {
        if (!array_key_exists(\$type, " . $this->getPeerClassname() . "::\$fieldNames)) {
            throw new PropelException('Method getFieldNames() expects the parameter \$type to be one of the class constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME, BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM. ' . \$type . ' was given.');
        }

        return " . $this->getPeerClassname() . "::\$fieldNames[\$type];
    }
";
    } // addGetFieldNames()

    protected function addTranslateFieldName(&$script)
    {
        $script .= "
    /**
     * Translates a fieldname to another type
     *
     * @param      string \$name field name
     * @param      string \$fromType One of the class type constants BasePeer::TYPE_PHPNAME, BasePeer::TYPE_STUDLYPHPNAME
     *                         BasePeer::TYPE_COLNAME, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_NUM
     * @param      string \$toType   One of the class type constants
     * @return string          translated name of the field.
     * @throws PropelException - if the specified name could not be found in the fieldname mappings.
     */
    public static function translateFieldName(\$name, \$fromType, \$toType)
    {
        \$toNames = " . $this->getPeerClassname() . "::getFieldNames(\$toType);
        \$key = isset(" . $this->getPeerClassname() . "::\$fieldKeys[\$fromType][\$name]) ? " . $this->getPeerClassname() . "::\$fieldKeys[\$fromType][\$name] : null;
        if (\$key === null) {
            throw new PropelException(\"'\$name' could not be found in the field names of type '\$fromType'. These are: \" . print_r(" . $this->getPeerClassname() . "::\$fieldKeys[\$fromType], true));
        }

        return \$toNames[\$key];
    }
";
    } // addTranslateFieldName()

    /**
     * Adds the getValueSets() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetValueSets(&$script)
    {
        $this->declareClassFromBuilder($this->getTableMapBuilder());
        $callingClass = $this->getStubPeerBuilder()->getClassname();
        $script .= "
    /**
     * Gets the list of values for all ENUM columns
     * @return array
     */
    public static function getValueSets()
    {
      return {$callingClass}::\$enumValueSets;
    }
";
    }

    /**
     * Adds the getValueSet() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetValueSet(&$script)
    {
        $this->declareClassFromBuilder($this->getTableMapBuilder());
        $script .= "
    /**
     * Gets the list of values for an ENUM column
     *
     * @param string \$colname The ENUM column name.
     *
     * @return array list of possible values for the column
     */
    public static function getValueSet(\$colname)
    {
        \$valueSets = " . $this->getPeerClassname() . "::getValueSets();

        if (!isset(\$valueSets[\$colname])) {
            throw new PropelException(sprintf('Column \"%s\" has no ValueSet.', \$colname));
        }

        return \$valueSets[\$colname];
    }
";
    }

    /**
     * Adds the getSqlValueForEnum() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetSqlValueForEnum(&$script)
    {
        $this->declareClassFromBuilder($this->getTableMapBuilder());
        $script .= "
    /**
     * Gets the SQL value for the ENUM column value
     *
     * @param string \$colname ENUM column name.
     * @param string \$enumVal ENUM value.
     *
     * @return int SQL value
     */
    public static function getSqlValueForEnum(\$colname, \$enumVal)
    {
        \$values = " . $this->getPeerClassname() . "::getValueSet(\$colname);
        if (!in_array(\$enumVal, \$values)) {
            throw new PropelException(sprintf('Value \"%s\" is not accepted in this enumerated column', \$colname));
        }

        return array_search(\$enumVal, \$values);
    }
";
    }

    /**
     * Adds methods for ENUM columns.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addEnumMethods(&$script)
    {
        foreach ($this->getTable()->getColumns() as $col) {
            /* @var $col Column */
            if ($col->isEnumType()) {
                $script .= "
    /**
     * Gets the SQL value for " . $col->getPhpName() . " ENUM value
     *
     * @param  string \$enumVal ENUM value to get SQL value for
     * @return int SQL value
     */
    public static function get{$col->getPhpName()}SqlValue(\$enumVal)
    {
        return {$this->getPeerClassname()}::getSqlValueForEnum({$this->getColumnConstant($col)}, \$enumVal);
    }
";
            }
        }
    }

    /**
     * Adds the buildTableMap() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addBuildTableMap(&$script)
    {
        $this->declareClassFromBuilder($this->getTableMapBuilder());
        $script .= "
    /**
     * Add a TableMap instance to the database for this peer class.
     */
    public static function buildTableMap()
    {
      \$dbMap = Propel::getDatabaseMap(" . $this->getClassname() . "::DATABASE_NAME);
      if (!\$dbMap->hasTable(" . $this->getClassname() . "::TABLE_NAME)) {
        \$dbMap->addTableObject(new " . $this->getTableMapClass() . "());
      }
    }
";
    }

    /**
     * Adds the CLASSKEY_* and CLASSNAME_* constants used for inheritance.
     *
     * @param string &$script The script will be modified in this method.
     */
    public function addInheritanceColumnConstants(&$script)
    {
        if ($this->getTable()->getChildrenColumn()) {

            $col = $this->getTable()->getChildrenColumn();
            $cfc = $col->getPhpName();

            if ($col->isEnumeratedClasses()) {

                if ($col->isPhpPrimitiveNumericType()) {
                    $quote = "";
                } else {
                    $quote = '"';
                }

                foreach ($col->getChildren() as $child) {
                    $childBuilder = $this->getMultiExtendObjectBuilder();
                    $childBuilder->setChild($child);
                    $fqcn = addslashes($childBuilder->getFullyQualifiedClassname());

                    $script .= "
    /** A key representing a particular subclass */
    const CLASSKEY_" . strtoupper($child->getKey()) . " = '" . $child->getKey() . "';
";

                    if (strtoupper($child->getClassname()) != strtoupper($child->getKey())) {
                        $script .= "
    /** A key representing a particular subclass */
    const CLASSKEY_" . strtoupper($child->getClassname()) . " = '" . $child->getKey() . "';
";
                    }

                    $script .= "
    /** A class that can be returned by this peer. */
    const CLASSNAME_" . strtoupper($child->getKey()) . " = '" . $fqcn . "';
";
                } /* foreach children */
            } /* if col->isenumerated...() */
        } /* if table->getchildrencolumn() */
    } //

    /**
     * Adds the alias() utility method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addAlias(&$script)
    {
        $script .= "
    /**
     * Convenience method which changes table.column to alias.column.
     *
     * Using this method you can maintain SQL abstraction while using column aliases.
     * <code>
     *		\$c->addAlias(\"alias1\", TablePeer::TABLE_NAME);
     *		\$c->addJoin(TablePeer::alias(\"alias1\", TablePeer::PRIMARY_KEY_COLUMN), TablePeer::PRIMARY_KEY_COLUMN);
     * </code>
     * @param      string \$alias The alias for the current table.
     * @param      string \$column The column name for current table. (i.e. " . $this->getPeerClassname() . "::COLUMN_NAME).
     * @return string
     */
    public static function alias(\$alias, \$column)
    {
        return str_replace(" . $this->getPeerClassname() . "::TABLE_NAME.'.', \$alias.'.', \$column);
    }
";
    } // addAliasMethod

    /**
     * Adds the addSelectColumns() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addAddSelectColumns(&$script)
    {
        $script .= "
    /**
     * Add all the columns needed to create a new object.
     *
     * Note: any columns that were marked with lazyLoad=\"true\" in the
     * XML schema will not be added to the select list and only loaded
     * on demand.
     *
     * @param      Criteria \$criteria object containing the columns to add.
     * @param      string   \$alias    optional table alias
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function addSelectColumns(Criteria \$criteria, \$alias = null)
    {
        if (null === \$alias) {";
        foreach ($this->getTable()->getColumns() as $col) {
            if (!$col->isLazyLoad()) {
                $script .= "
            \$criteria->addSelectColumn(" . $this->getPeerClassname() . "::" . $this->getColumnName($col) . ");";
            } // if !col->isLazyLoad
        } // foreach
        $script .= "
        } else {";
        foreach ($this->getTable()->getColumns() as $col) {
            if (!$col->isLazyLoad()) {
                $script .= "
            \$criteria->addSelectColumn(\$alias . '." . $col->getName() . "');";
            } // if !col->isLazyLoad
        } // foreach
        $script .= "
        }";
        $script .= "
    }
";
    } // addAddSelectColumns()

    /**
     * Adds the doCount() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoCount(&$script)
    {
        $script .= "
    /**
     * Returns the number of rows matching criteria.
     *
     * @param      Criteria \$criteria
     * @param      boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
     * @param      PropelPDO \$con
     * @return int Number of matching rows.
     */
    public static function doCount(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null)
    {
        // we may modify criteria, so copy it first
        \$criteria = clone \$criteria;

        // We need to set the primary table name, since in the case that there are no WHERE columns
        // it will be impossible for the BasePeer::createSelectSql() method to determine which
        // tables go into the FROM clause.
        \$criteria->setPrimaryTableName(" . $this->getPeerClassname() . "::TABLE_NAME);

        if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
            \$criteria->setDistinct();
        }

        if (!\$criteria->hasSelectClause()) {
            " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        }

        \$criteria->clearOrderByColumns(); // ORDER BY won't ever affect the count
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME); // Set the correct dbName

        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }";

        // apply behaviors
        $this->applyBehaviorModifier('preSelect', $script);

        $script .= "
        // BasePeer returns a PDOStatement
        \$stmt = " . $this->basePeerClassname . "::doCount(\$criteria, \$con);

        if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$count = (int) \$row[0];
        } else {
            \$count = 0; // no rows returned; we infer that means 0 matches.
        }
        \$stmt->closeCursor();

        return \$count;
    }";
    }

    /**
     * Adds the doSelectOne() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoSelectOne(&$script)
    {
        $script .= "
    /**
     * Selects one object from the DB.
     *
     * @param      Criteria \$criteria object used to create the SELECT statement.
     * @param      PropelPDO \$con
     * @return "               . $this->getObjectClassname() . "
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function doSelectOne(Criteria \$criteria, PropelPDO \$con = null)
    {
        \$critcopy = clone \$criteria;
        \$critcopy->setLimit(1);
        \$objects = " . $this->getPeerClassname() . "::doSelect(\$critcopy, \$con);
        if (\$objects) {
            return \$objects[0];
        }

        return null;
    }";
    }

    /**
     * Adds the doSelect() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoSelect(&$script)
    {
        $script .= "
    /**
     * Selects several row from the DB.
     *
     * @param      Criteria \$criteria The Criteria object used to build the SELECT statement.
     * @param      PropelPDO \$con
     * @return array           Array of selected Objects
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function doSelect(Criteria \$criteria, PropelPDO \$con = null)
    {
        return " . $this->getPeerClassname() . "::populateObjects(" . $this->getPeerClassname() . "::doSelectStmt(\$criteria, \$con));
    }";
    }

    /**
     * Adds the doSelectStmt() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoSelectStmt(&$script)
    {

        $script .= "
    /**
     * Prepares the Criteria object and uses the parent doSelect() method to execute a PDOStatement.
     *
     * Use this method directly if you want to work with an executed statement directly (for example
     * to perform your own object hydration).
     *
     * @param      Criteria \$criteria The Criteria object used to build the SELECT statement.
     * @param      PropelPDO \$con The connection to use
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     * @return PDOStatement The executed PDOStatement object.
     * @see        " . $this->basePeerClassname . "::doSelect()
     */
    public static function doSelectStmt(Criteria \$criteria, PropelPDO \$con = null)
    {
        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }

        if (!\$criteria->hasSelectClause()) {
            \$criteria = clone \$criteria;
            " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        }

        // Set the correct dbName
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);";
        // apply behaviors
        if ($this->hasBehaviorModifier('preSelect')) {
            $this->applyBehaviorModifier('preSelect', $script);
        }
        $script .= "

        // BasePeer returns a PDOStatement
        return " . $this->basePeerClassname . "::doSelect(\$criteria, \$con);
    }";
    }

    /**
     * Adds the PHP code to return a instance pool key for the passed-in primary key variable names.
     *
     * @param array $pkphp An array of PHP var names / method calls representing complete pk.
     *
     * @return string
     */
    public function getInstancePoolKeySnippet($pkphp)
    {
        $pkphp = (array) $pkphp; // make it an array if it is not.
        $script = "";
        if (count($pkphp) > 1) {
            $script .= "serialize(array(";
            $i = 0;
            foreach ($pkphp as $pkvar) {
                $script .= ($i++ ? ', ' : '') . "(string) $pkvar";
            }
            $script .= "))";
        } else {
            $script .= "(string) " . $pkphp[0];
        }

        return $script;
    }

    /**
     * Creates a convenience method to add objects to an instance pool.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addAddInstanceToPool(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Adds an object to the instance pool.
     *
     * Propel keeps cached copies of objects in an instance pool when they are retrieved
     * from the database.  In some cases -- especially when you override doSelect*()
     * methods in your stub classes -- you may need to explicitly add objects
     * to the cache in order to ensure that the same objects are always returned by doSelect*()
     * and retrieveByPK*() calls.
     *
     * @param " . $this->getObjectClassname() . " \$obj A " . $this->getObjectClassname() . " object.
     * @param      string \$key (optional) key to use for instance map (for performance boost if key was already calculated externally).
     */
    public static function addInstanceToPool(\$obj, \$key = null)
    {
        if (Propel::isInstancePoolingEnabled()) {
            if (\$key === null) {";

        $pks = $this->getTable()->getPrimaryKey();

        $php = array();
        foreach ($pks as $pk) {
            if ($pk->isTemporalType()) {
                $php[] = '$obj->get' . $pk->getPhpName() . "('U')";
            } else {
                $php[] = '$obj->get' . $pk->getPhpName() . '()';
            }
        }
        $script .= "
                \$key = " . $this->getInstancePoolKeySnippet($php) . ";";
        $script .= "
            } // if key === null
            " . $this->getPeerClassname() . "::\$instances[\$key] = \$obj;
        }
    }
";
    } // addAddInstanceToPool()

    /**
     *  Creates a convenience method to remove objects form an instance pool.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addRemoveInstanceFromPool(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Removes an object from the instance pool.
     *
     * Propel keeps cached copies of objects in an instance pool when they are retrieved
     * from the database.  In some cases -- especially when you override doDelete
     * methods in your stub classes -- you may need to explicitly remove objects
     * from the cache in order to prevent returning objects that no longer exist.
     *
     * @param      mixed \$value A " . $this->getObjectClassname() . " object or a primary key value.
     *
     * @return void
     * @throws PropelException - if the value is invalid.
     */
    public static function removeInstanceFromPool(\$value)
    {";
        $script .= "
        if (Propel::isInstancePoolingEnabled() && \$value !== null) {";
        $pks = $table->getPrimaryKey();

        $script .= "
            if (is_object(\$value) && \$value instanceof " . $this->getObjectClassname() . ") {";

        $php = array();
        foreach ($pks as $pk) {
            $php[] = '$value->get' . $pk->getPhpName() . '()';
        }
        $script .= "
                \$key = " . $this->getInstancePoolKeySnippet($php) . ";";

        $script .= "
            } elseif (" . (count($pks) > 1 ? "is_array(\$value) && count(\$value) === " . count($pks) : "is_scalar(\$value)") . ") {
                // assume we've been passed a primary key";

        if (count($pks) > 1) {
            $php = array();
            for ($i = 0; $i < count($pks); $i++) {
                $php[] = "\$value[$i]";
            }
        } else {
            $php = '$value';
        }
        $script .= "
                \$key = " . $this->getInstancePoolKeySnippet($php) . ";";
        $script .= "
            } else {
                \$e = new PropelException(\"Invalid value passed to removeInstanceFromPool().  Expected primary key or " . $this->getObjectClassname() . " object; got \" . (is_object(\$value) ? get_class(\$value) . ' object.' : var_export(\$value,true)));
                throw \$e;
            }

            unset(" . $this->getPeerClassname() . "::\$instances[\$key]);
        }
    } // removeInstanceFromPool()
";
    } // addRemoveFromInstancePool()

    /**
     * Adds method to clear the instance pool.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addClearInstancePool(&$script)
    {
        $script .= "
    /**
     * Clear the instance pool.
     *
     * @return void
     */
    public static function clearInstancePool(\$and_clear_all_references = false)
    {
      if (\$and_clear_all_references) {
        foreach (" . $this->getPeerClassname() . "::\$instances as \$instance) {
          \$instance->clearAllReferences(true);
        }
      }
        " . $this->getPeerClassname() . "::\$instances = array();
    }
    ";
    }

    /**
     * Adds method to clear the instance pool of related tables.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addClearRelatedInstancePool(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Method to invalidate the instance pool of all tables related to " . $table->getName() . "
     * by a foreign key with ON DELETE CASCADE
     */
    public static function clearRelatedInstancePool()
    {";
        // Handle ON DELETE CASCADE for updating instance pool

        foreach ($table->getReferrers() as $fk) {

            // $fk is the foreign key in the other table, so localTableName will
            // actually be the table name of other table
            $tblFK = $fk->getTable();

            $joinedTablePeerBuilder = $this->getNewStubPeerBuilder($tblFK);
            $this->declareClassFromBuilder($joinedTablePeerBuilder);
            $tblFKPackage = $joinedTablePeerBuilder->getStubPeerBuilder()->getPackage();

            if (!$tblFK->isForReferenceOnly()) {
                // we can't perform operations on tables that are
                // not within the schema (i.e. that we have no map for, etc.)

                if ($fk->getOnDelete() == ForeignKey::CASCADE || $fk->getOnDelete() == ForeignKey::SETNULL) {
                    $script .= "
        // Invalidate objects in " . $joinedTablePeerBuilder->getClassname() . " instance pool,
        // since one or more of them may be deleted by ON DELETE CASCADE/SETNULL rule.
        " . $joinedTablePeerBuilder->getClassname() . "::clearInstancePool();";
                } // if fk is on delete cascade
            } // if (! for ref only)
        } // foreach
        $script .= "
    }
";
    }

    /**
     * Adds method to get an the instance from the pool, given a key.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetInstanceFromPool(&$script)
    {
        $script .= "
    /**
     * Retrieves a string version of the primary key from the DB resultset row that can be used to uniquely identify a row in this table.
     *
     * For tables with a single-column primary key, that simple pkey value will be returned.  For tables with
     * a multi-column primary key, a serialize()d version of the primary key will be returned.
     *
     * @param      string \$key The key (@see getPrimaryKeyHash()) for this instance.
     * @return " . $this->getObjectClassname() . " Found object or null if 1) no instance exists for specified key or 2) instance pooling has been disabled.
     * @see        getPrimaryKeyHash()
     */
    public static function getInstanceFromPool(\$key)
    {
        if (Propel::isInstancePoolingEnabled()) {
            if (isset(" . $this->getPeerClassname() . "::\$instances[\$key])) {
                return " . $this->getPeerClassname() . "::\$instances[\$key];
            }
        }

        return null; // just to be explicit
    }
    ";
    }

    /**
     * Adds method to get a version of the primary key that can be used as a unique key for identifier map.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetPrimaryKeyHash(&$script)
    {
        $script .= "
    /**
     * Retrieves a string version of the primary key from the DB resultset row that can be used to uniquely identify a row in this table.
     *
     * For tables with a single-column primary key, that simple pkey value will be returned.  For tables with
     * a multi-column primary key, a serialize()d version of the primary key will be returned.
     *
     * @param      array \$row PropelPDO resultset row.
     * @param      int \$startcol The 0-based offset for reading from the resultset row.
     * @return string A string version of PK or null if the components of primary key in result array are all null.
     */
    public static function getPrimaryKeyHashFromRow(\$row, \$startcol = 0)
    {";

        // We have to iterate through all the columns so that we know the offset of the primary
        // key columns.
        $n = 0;
        $pk = array();
        $cond = array();
        foreach ($this->getTable()->getColumns() as $col) {
            if (!$col->isLazyLoad()) {
                if ($col->isPrimaryKey()) {
                    $part = $n ? "\$row[\$startcol + $n]" : "\$row[\$startcol]";
                    $cond[] = $part . " === null";
                    $pk[] = $part;
                }
                $n++;
            }
        }

        $script .= "
        // If the PK cannot be derived from the row, return null.
        if (" . implode(' && ', $cond) . ") {
            return null;
        }

        return " . $this->getInstancePoolKeySnippet($pk) . ";
    }
";
    } // addGetPrimaryKeyHash

    /**
     * Adds method to get the primary key from a row
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetPrimaryKeyFromRow(&$script)
    {
        $script .= "
    /**
     * Retrieves the primary key from the DB resultset row
     * For tables with a single-column primary key, that simple pkey value will be returned.  For tables with
     * a multi-column primary key, an array of the primary key columns will be returned.
     *
     * @param      array \$row PropelPDO resultset row.
     * @param      int \$startcol The 0-based offset for reading from the resultset row.
     * @return mixed The primary key of the row
     */
    public static function getPrimaryKeyFromRow(\$row, \$startcol = 0)
    {";

        // We have to iterate through all the columns so that we know the offset of the primary
        // key columns.
        $table = $this->getTable();
        $n = 0;
        $pks = array();
        foreach ($table->getColumns() as $col) {
            if (!$col->isLazyLoad()) {
                if ($col->isPrimaryKey()) {
                    $pk = '(' . $col->getPhpType() . ') ' . ($n ? "\$row[\$startcol + $n]" : "\$row[\$startcol]");
                    if ($table->hasCompositePrimaryKey()) {
                        $pks[] = $pk;
                    }
                }
                $n++;
            }
        }
        if ($table->hasCompositePrimaryKey()) {
            $script .= "

        return array(" . implode($pks, ', ') . ");";
        } else {
            $script .= "

        return " . $pk . ";";
        }
        $script .= "
    }
    ";
    } // addGetPrimaryKeyFromRow

    /**
     * Adds the populateObjects() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addPopulateObjects(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * The returned array will contain objects of the default type or
     * objects that inherit from the default.
     *
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function populateObjects(PDOStatement \$stmt)
    {
        \$results = array();
    ";
        if (!$table->getChildrenColumn()) {
            $script .= "
        // set the class once to avoid overhead in the loop
        \$cls = " . $this->getPeerClassname() . "::getOMClass();";
        }

        $script .= "
        // populate the object(s)
        while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$key = " . $this->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, 0);
            if (null !== (\$obj = " . $this->getPeerClassname() . "::getInstanceFromPool(\$key))) {
                // We no longer rehydrate the object, since this can cause data loss.
                // See http://www.propelorm.org/ticket/509
                // \$obj->hydrate(\$row, 0, true); // rehydrate
                \$results[] = \$obj;
            } else {";
        if ($table->getChildrenColumn()) {
            $script .= "
                // class must be set each time from the record row
                \$cls = " . $this->getPeerClassname() . "::getOMClass(\$row, 0);
                \$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
                " . $this->buildObjectInstanceCreationCode('$obj', '$cls') . "
                \$obj->hydrate(\$row);
                \$results[] = \$obj;
                " . $this->getPeerClassname() . "::addInstanceToPool(\$obj, \$key);";
        } else {
            $script .= "
                " . $this->buildObjectInstanceCreationCode('$obj', '$cls') . "
                \$obj->hydrate(\$row);
                \$results[] = \$obj;
                " . $this->getPeerClassname() . "::addInstanceToPool(\$obj, \$key);";
        }
        $script .= "
            } // if key exists
        }
        \$stmt->closeCursor();

        return \$results;
    }";
    }

    /**
     * Adds the populateObject() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addPopulateObject(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Populates an object of the default type or an object that inherit from the default.
     *
     * @param      array \$row PropelPDO resultset row.
     * @param      int \$startcol The 0-based offset for reading from the resultset row.
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     * @return array (" . $this->getStubObjectBuilder()->getClassName() . " object, last column rank)
     */
    public static function populateObject(\$row, \$startcol = 0)
    {
        \$key = " . $this->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, \$startcol);
        if (null !== (\$obj = " . $this->getPeerClassname() . "::getInstanceFromPool(\$key))) {
            // We no longer rehydrate the object, since this can cause data loss.
            // See http://www.propelorm.org/ticket/509
            // \$obj->hydrate(\$row, \$startcol, true); // rehydrate
            \$col = \$startcol + " . $this->getPeerClassname() . "::NUM_HYDRATE_COLUMNS;";
        if ($table->isAbstract()) {
            $script .= "
        } elseif (null == \$key) {
            // empty resultset, probably from a left join
            // since this table is abstract, we can't hydrate an empty object
            \$obj = null;
            \$col = \$startcol + " . $this->getPeerClassname() . "::NUM_HYDRATE_COLUMNS;";
        }
        $script .= "
        } else {";
        if (!$table->getChildrenColumn()) {
            $script .= "
            \$cls = " . $this->getPeerClassname() . "::OM_CLASS;";
        } else {
            $script .= "
            \$cls = " . $this->getPeerClassname() . "::getOMClass(\$row, \$startcol);";
        }
        $script .= "
            \$obj = new \$cls();
            \$col = \$obj->hydrate(\$row, \$startcol);
            " . $this->getPeerClassname() . "::addInstanceToPool(\$obj, \$key);
        }

        return array(\$obj, \$col);
    }
";
    }

    /**
     * Adds a getOMClass() for non-abstract tables that have inheritance.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetOMClass_Inheritance(&$script)
    {
        $col = $this->getTable()->getChildrenColumn();
        $script .= "
    /**
     * The returned Class will contain objects of the default type or
     * objects that inherit from the default.
     *
     * @param      array \$row PropelPDO result row.
     * @param      int \$colnum Column to examine for OM class information (first is 0).
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function getOMClass(\$row = 0, \$colnum = 0)
    {
        try {
";
        if ($col->isEnumeratedClasses()) {
            $script .= "
            \$omClass = null;
            \$classKey = \$row[\$colnum + " . ($col->getPosition() - 1) . "];

            switch (\$classKey) {
";
            foreach ($col->getChildren() as $child) {
                $script .= "
                case " . $this->getPeerClassname() . "::CLASSKEY_" . strtoupper($child->getKey()) . ":
                    \$omClass = " . $this->getPeerClassname() . "::CLASSNAME_" . strtoupper($child->getKey()) . ";
                    break;
";
            } /* foreach */
            $script .= "
                default:
                    \$omClass = " . $this->getPeerClassname() . "::OM_CLASS;
";
            $script .= "
            } // switch
";
        } else { /* if not enumerated */
            $script .= "
            \$omClass = \$row[\$colnum + " . ($col->getPosition() - 1) . "];
            \$omClass = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
";
        }
        $script .= "
        } catch (Exception \$e) {
            throw new PropelException('Unable to get OM class.', \$e);
        }

        return \$omClass;
    }
";
    }

    /**
     * Adds a getOMClass() for non-abstract tables that do note use inheritance.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetOMClass_NoInheritance(&$script)
    {
        $script .= "
    /**
     * The class that the Peer will make instances of.
     *
     *
     * @return string ClassName
     */
    public static function getOMClass(\$row = 0, \$colnum = 0)
    {
        return " . $this->getPeerClassname() . "::OM_CLASS;
    }
";
    }

    /**
     * Adds a getOMClass() signature for abstract tables that do not have inheritance.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetOMClass_NoInheritance_Abstract(&$script)
    {
        $script .= "
    /**
     * The class that the Peer will make instances of.
     *
     * This method must be overridden by the stub subclass, because
     * " . $this->getObjectClassname() . " is declared abstract in the schema.
     */";

        if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
            $script .= "
    abstract public static function getOMClass(\$row = 0, \$colnum = 0);
";
        } else {
            $script .= "
    public static function getOMClass(\$row = 0, \$colnum = 0)
    {
        throw new LogicException(\"You can't get OMClass for an abstract Peer class.\");
    }
";
        }
    }

    /**
     * Adds the doInsert() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoInsert(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Performs an INSERT on the database, given a " . $this->getObjectClassname() . " or Criteria object.
     *
     * @param      mixed \$values Criteria or " . $this->getObjectClassname() . " object containing data that is used to create the INSERT statement.
     * @param      PropelPDO \$con the PropelPDO connection to use
     * @return mixed           The new primary key.
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function doInsert(\$values, PropelPDO \$con = null)
    {
        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_WRITE);
        }

        if (\$values instanceof Criteria) {
            \$criteria = clone \$values; // rename for clarity
        } else {
            \$criteria = \$values->buildCriteria(); // build Criteria from " . $this->getObjectClassname() . " object
        }
";

        foreach ($table->getColumns() as $col) {
            $cfc = $col->getPhpName();
            if ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && !$table->isAllowPkInsert()) {
                $script .= "
        if (\$criteria->containsKey(" . $this->getColumnConstant($col) . ") && \$criteria->keyContainsValue(" . $this->getColumnConstant($col) . ") ) {
            throw new PropelException('Cannot insert a value for auto-increment primary key ('." . $this->getColumnConstant($col) . ".')');
        }
";
                if (!$this->getPlatform()->supportsInsertNullPk()) {
                    $script .= "
        // remove pkey col since this table uses auto-increment and passing a null value for it is not valid
        \$criteria->remove(" . $this->getColumnConstant($col) . ");
";
                }
            } elseif ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none" && $table->isAllowPkInsert() && !$this->getPlatform()->supportsInsertNullPk()) {
                $script .= "
        // remove pkey col if it is null since this table does not accept that
        if (\$criteria->containsKey(" . $this->getColumnConstant($col) . ") && !\$criteria->keyContainsValue(" . $this->getColumnConstant($col) . ") ) {
            \$criteria->remove(" . $this->getColumnConstant($col) . ");
        }
";
            }
        }
        $script .= "

        // Set the correct dbName
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);

        try {
            // use transaction because \$criteria could contain info
            // for more than one table (I guess, conceivably)
            \$con->beginTransaction();
            \$pk = " . $this->basePeerClassname . "::doInsert(\$criteria, \$con);
            \$con->commit();
        } catch (Exception \$e) {
            \$con->rollBack();
            throw \$e;
        }

        return \$pk;
    }
";
    }

    /**
     * Adds the doUpdate() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoUpdate(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Performs an UPDATE on the database, given a " . $this->getObjectClassname() . " or Criteria object.
     *
     * @param      mixed \$values Criteria or " . $this->getObjectClassname() . " object containing data that is used to create the UPDATE statement.
     * @param      PropelPDO \$con The connection to use (specify PropelPDO connection object to exert more control over transactions).
     * @return int             The number of affected rows (if supported by underlying database driver).
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function doUpdate(\$values, PropelPDO \$con = null)
    {
        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_WRITE);
        }

        \$selectCriteria = new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME);

        if (\$values instanceof Criteria) {
            \$criteria = clone \$values; // rename for clarity
";
        foreach ($table->getColumns() as $col) {
            if ($col->isPrimaryKey()) {
                $script .= "
            \$comparison = \$criteria->getComparison(" . $this->getColumnConstant($col) . ");
            \$value = \$criteria->remove(" . $this->getColumnConstant($col) . ");
            if (\$value) {
                \$selectCriteria->add(" . $this->getColumnConstant($col) . ", \$value, \$comparison);
            } else {
                \$selectCriteria->setPrimaryTableName(" . $this->getPeerClassname() . "::TABLE_NAME);
            }
";
            } /* if col is prim key */
        } /* foreach */

        $script .= "
        } else { // \$values is " . $this->getObjectClassname() . " object
            \$criteria = \$values->buildCriteria(); // gets full criteria
            \$selectCriteria = \$values->buildPkeyCriteria(); // gets criteria w/ primary key(s)
        }

        // set the correct dbName
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);

        return {$this->basePeerClassname}::doUpdate(\$selectCriteria, \$criteria, \$con);
    }
";
    }

    /**
     * Adds the doDeleteAll() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoDeleteAll(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Deletes all rows from the " . $table->getName() . " table.
     *
     * @param      PropelPDO \$con the connection to use
     * @return int             The number of affected rows (if supported by underlying database driver).
     * @throws PropelException
     */
    public static function doDeleteAll(PropelPDO \$con = null)
    {
        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_WRITE);
        }
        \$affectedRows = 0; // initialize var to track total num of affected rows
        try {
            // use transaction because \$criteria could contain info
            // for more than one table or we could emulating ON DELETE CASCADE, etc.
            \$con->beginTransaction();
            ";
        if ($this->isDeleteCascadeEmulationNeeded()) {
            $script .= "\$affectedRows += " . $this->getPeerClassname() . "::doOnDeleteCascade(new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME), \$con);
            ";
        }
        if ($this->isDeleteSetNullEmulationNeeded()) {
            $script .= $this->getPeerClassname() . "::doOnDeleteSetNull(new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME), \$con);
            ";
        }
        $script .= "\$affectedRows += {$this->basePeerClassname}::doDeleteAll(" . $this->getPeerClassname() . "::TABLE_NAME, \$con, " . $this->getPeerClassname() . "::DATABASE_NAME);
            // Because this db requires some delete cascade/set null emulation, we have to
            // clear the cached instance *after* the emulation has happened (since
            // instances get re-added by the select statement contained therein).
            " . $this->getPeerClassname() . "::clearInstancePool();
            " . $this->getPeerClassname() . "::clearRelatedInstancePool();
            \$con->commit();

            return \$affectedRows;
        } catch (Exception \$e) {
            \$con->rollBack();
            throw \$e;
        }
    }
";
    }

    /**
     * Adds the doDelete() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoDelete(&$script)
    {
        $table = $this->getTable();
        $emulateCascade = $this->isDeleteCascadeEmulationNeeded() || $this->isDeleteSetNullEmulationNeeded();
        $script .= "
    /**
     * Performs a DELETE on the database, given a " . $this->getObjectClassname() . " or Criteria object OR a primary key value.
     *
     * @param      mixed \$values Criteria or " . $this->getObjectClassname() . " object or primary key or array of primary keys
     *              which is used to create the DELETE statement
     * @param      PropelPDO \$con the connection to use
     * @return int The number of affected rows (if supported by underlying database driver).  This includes CASCADE-related rows
     *				if supported by native driver or if emulated using Propel.
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
     public static function doDelete(\$values, PropelPDO \$con = null)
     {
        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_WRITE);
        }

        if (\$values instanceof Criteria) {";
        if (!$emulateCascade) {
            $script .= "
            // invalidate the cache for all objects of this type, since we have no
            // way of knowing (without running a query) what objects should be invalidated
            // from the cache based on this Criteria.
            " . $this->getPeerClassname() . "::clearInstancePool();";
        }
        $script .= "
            // rename for clarity
            \$criteria = clone \$values;
        } elseif (\$values instanceof " . $this->getObjectClassname() . ") { // it's a model object";
        if (!$emulateCascade) {
            $script .= "
            // invalidate the cache for this single object
            " . $this->getPeerClassname() . "::removeInstanceFromPool(\$values);";
        }
        if (count($table->getPrimaryKey()) > 0) {
            $script .= "
            // create criteria based on pk values
            \$criteria = \$values->buildPkeyCriteria();";
        } else {
            $script .= "
            // create criteria based on pk value
            \$criteria = \$values->buildCriteria();";
        }

        $script .= "
        } else { // it's a primary key, or an array of pks";
        $script .= "
            \$criteria = new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME);";

        if (count($table->getPrimaryKey()) === 1) {
            $pkey = $table->getPrimaryKey();
            $col = array_shift($pkey);
            $script .= "
            \$criteria->add(" . $this->getColumnConstant($col) . ", (array) \$values, Criteria::IN);";
            if (!$emulateCascade) {
                $script .= "
            // invalidate the cache for this object(s)
            foreach ((array) \$values as \$singleval) {
                " . $this->getPeerClassname() . "::removeInstanceFromPool(\$singleval);
            }";
            }
        } else {
            $script .= "
            // primary key is composite; we therefore, expect
            // the primary key passed to be an array of pkey values
            if (count(\$values) == count(\$values, COUNT_RECURSIVE)) {
                // array is not multi-dimensional
                \$values = array(\$values);
            }
            foreach (\$values as \$value) {";
            $i = 0;
            foreach ($table->getPrimaryKey() as $col) {
                if ($i == 0) {
                    $script .= "
                \$criterion = \$criteria->getNewCriterion(" . $this->getColumnConstant($col) . ", \$value[$i]);";
                } else {
                    $script .= "
                \$criterion->addAnd(\$criteria->getNewCriterion(" . $this->getColumnConstant($col) . ", \$value[$i]));";
                }
                $i++;
            }
            $script .= "
                \$criteria->addOr(\$criterion);";
            if (!$emulateCascade) {
                $script .= "
                // we can invalidate the cache for this single PK
                " . $this->getPeerClassname() . "::removeInstanceFromPool(\$value);";
            }
            $script .= "
            }";
        } /* if count(table->getPrimaryKeys()) */

        $script .= "
        }

        // Set the correct dbName
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);

        \$affectedRows = 0; // initialize var to track total num of affected rows

        try {
            // use transaction because \$criteria could contain info
            // for more than one table or we could emulating ON DELETE CASCADE, etc.
            \$con->beginTransaction();
            ";

        if ($this->isDeleteCascadeEmulationNeeded()) {
            $script .= "
            // cloning the Criteria in case it's modified by doSelect() or doSelectStmt()
            \$c = clone \$criteria;
            \$affectedRows += " . $this->getPeerClassname() . "::doOnDeleteCascade(\$c, \$con);
            ";
        }
        if ($this->isDeleteSetNullEmulationNeeded()) {
            $script .= "
            // cloning the Criteria in case it's modified by doSelect() or doSelectStmt()
            \$c = clone \$criteria;
            " . $this->getPeerClassname() . "::doOnDeleteSetNull(\$c, \$con);
            ";
        }

        if ($emulateCascade) {
            $script .= "
            // Because this db requires some delete cascade/set null emulation, we have to
            // clear the cached instance *after* the emulation has happened (since
            // instances get re-added by the select statement contained therein).
            if (\$values instanceof Criteria) {
                " . $this->getPeerClassname() . "::clearInstancePool();
            } elseif (\$values instanceof " . $this->getObjectClassname() . ") { // it's a model object
                " . $this->getPeerClassname() . "::removeInstanceFromPool(\$values);
            } else { // it's a primary key, or an array of pks
                foreach ((array) \$values as \$singleval) {
                    " . $this->getPeerClassname() . "::removeInstanceFromPool(\$singleval);
                }
            }
            ";
        }

        $script .= "
            \$affectedRows += {$this->basePeerClassname}::doDelete(\$criteria, \$con);
            " . $this->getPeerClassname() . "::clearRelatedInstancePool();
            \$con->commit();

            return \$affectedRows;
        } catch (Exception \$e) {
            \$con->rollBack();
            throw \$e;
        }
    }
";
    }

    /**
     * Adds the doOnDeleteCascade() method, which provides ON DELETE CASCADE emulation.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoOnDeleteCascade(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * This is a method for emulating ON DELETE CASCADE for DBs that don't support this
     * feature (like MySQL or SQLite).
     *
     * This method is not very speedy because it must perform a query first to get
     * the implicated records and then perform the deletes by calling those Peer classes.
     *
     * This method should be used within a transaction if possible.
     *
     * @param      Criteria \$criteria
     * @param      PropelPDO \$con
     * @return int The number of affected rows (if supported by underlying database driver).
     */
    protected static function doOnDeleteCascade(Criteria \$criteria, PropelPDO \$con)
    {
        // initialize var to track total num of affected rows
        \$affectedRows = 0;

        // first find the objects that are implicated by the \$criteria
        \$objects = " . $this->getPeerClassname() . "::doSelect(\$criteria, \$con);
        foreach (\$objects as \$obj) {
";

        foreach ($table->getReferrers() as $fk) {

            // $fk is the foreign key in the other table, so localTableName will
            // actually be the table name of other table
            $tblFK = $fk->getTable();

            $joinedTablePeerBuilder = $this->getNewPeerBuilder($tblFK);
            $tblFKPackage = $joinedTablePeerBuilder->getStubPeerBuilder()->getPackage();

            if (!$tblFK->isForReferenceOnly()) {
                // we can't perform operations on tables that are
                // not within the schema (i.e. that we have no map for, etc.)

                $fkClassName = $joinedTablePeerBuilder->getObjectClassname();

                if ($fk->getOnDelete() == ForeignKey::CASCADE) {

                    // backwards on purpose
                    $columnNamesF = $fk->getLocalColumns();
                    $columnNamesL = $fk->getForeignColumns();

                    $script .= "

            // delete related $fkClassName objects
            \$criteria = new Criteria(" . $joinedTablePeerBuilder->getPeerClassname() . "::DATABASE_NAME);
            ";
                    for ($x = 0, $xlen = count($columnNamesF); $x < $xlen; $x++) {
                        $columnFK = $tblFK->getColumn($columnNamesF[$x]);
                        $columnL = $table->getColumn($columnNamesL[$x]);

                        $script .= "
            \$criteria->add(" . $joinedTablePeerBuilder->getColumnConstant($columnFK) . ", \$obj->get" . $columnL->getPhpName() . "());";
                    }

                    $script .= "
            \$affectedRows += " . $joinedTablePeerBuilder->getPeerClassname() . "::doDelete(\$criteria, \$con);";
                } // if cascade && fkey table name != curr table name

            } // if not for ref only
        } // foreach foreign keys
        $script .= "
        }

        return \$affectedRows;
    }
";
    } // end addDoOnDeleteCascade

    /**
     * Adds the doOnDeleteSetNull() method, which provides ON DELETE SET NULL emulation.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoOnDeleteSetNull(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * This is a method for emulating ON DELETE SET NULL DBs that don't support this
     * feature (like MySQL or SQLite).
     *
     * This method is not very speedy because it must perform a query first to get
     * the implicated records and then perform the deletes by calling those Peer classes.
     *
     * This method should be used within a transaction if possible.
     *
     * @param      Criteria \$criteria
     * @param      PropelPDO \$con
     * @return void
     */
    protected static function doOnDeleteSetNull(Criteria \$criteria, PropelPDO \$con)
    {

        // first find the objects that are implicated by the \$criteria
        \$objects = " . $this->getPeerClassname() . "::doSelect(\$criteria, \$con);
        foreach (\$objects as \$obj) {
";

        // This logic is almost exactly the same as that in doOnDeleteCascade()
        // it may make sense to refactor this, provided that things don't
        // get too complicated.

        foreach ($table->getReferrers() as $fk) {

            // $fk is the foreign key in the other table, so localTableName will
            // actually be the table name of other table
            $tblFK = $fk->getTable();
            $refTablePeerBuilder = $this->getNewPeerBuilder($tblFK);

            if (!$tblFK->isForReferenceOnly()) {
                // we can't perform operations on tables that are
                // not within the schema (i.e. that we have no map for, etc.)

                $fkClassName = $refTablePeerBuilder->getObjectClassname();

                if ($fk->getOnDelete() == ForeignKey::SETNULL) {

                    // backwards on purpose
                    $columnNamesF = $fk->getLocalColumns();
                    $columnNamesL = $fk->getForeignColumns(); // should be same num as foreign
                    $script .= "
            // set fkey col in related $fkClassName rows to null
            \$selectCriteria = new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME);
            \$updateValues = new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME);";

                    for ($x = 0, $xlen = count($columnNamesF); $x < $xlen; $x++) {
                        $columnFK = $tblFK->getColumn($columnNamesF[$x]);
                        $columnL = $table->getColumn($columnNamesL[$x]);
                        $script .= "
            \$selectCriteria->add(" . $refTablePeerBuilder->getColumnConstant($columnFK) . ", \$obj->get" . $columnL->getPhpName() . "());
            \$updateValues->add(" . $refTablePeerBuilder->getColumnConstant($columnFK) . ", null);
";
                    }

                    $script .= "
            {$this->basePeerClassname}::doUpdate(\$selectCriteria, \$updateValues, \$con); // use BasePeer because generated Peer doUpdate() methods only update using pkey
";
                } // if setnull && fkey table name != curr table name
            } // if not for ref only
        } // foreach foreign keys

        $script .= "
        }
    }
";
    }

    /**
     * Adds the doValidate() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoValidate(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Validates all modified columns of given " . $this->getObjectClassname() . " object.
     * If parameter \$columns is either a single column name or an array of column names
     * than only those columns are validated.
     *
     * NOTICE: This does not apply to primary or foreign keys for now.
     *
     * @param " . $this->getObjectClassname() . " \$obj The object to validate.
     * @param      mixed \$cols Column name or array of column names.
     *
     * @return mixed TRUE if all columns are valid or the error message of the first invalid column.
     */
    public static function doValidate(\$obj, \$cols = null)
    {
        \$columns = array();

        if (\$cols) {
            \$dbMap = Propel::getDatabaseMap(" . $this->getPeerClassname() . "::DATABASE_NAME);
            \$tableMap = \$dbMap->getTable(" . $this->getPeerClassname() . "::TABLE_NAME);

            if (! is_array(\$cols)) {
                \$cols = array(\$cols);
            }

            foreach (\$cols as \$colName) {
                if (\$tableMap->hasColumn(\$colName)) {
                    \$get = 'get' . \$tableMap->getColumn(\$colName)->getPhpName();
                    \$columns[\$colName] = \$obj->\$get();
                }
            }
        } else {
";
        foreach ($table->getValidators() as $val) {
            $col = $val->getColumn();
            if (!$col->isAutoIncrement()) {
                $script .= "
        if (\$obj->isNew() || \$obj->isColumnModified(" . $this->getColumnConstant($col) . "))
            \$columns[" . $this->getColumnConstant($col) . "] = \$obj->get" . $col->getPhpName() . "();
";
            } // if
        } // foreach

        $script .= "
        }

        return {$this->basePeerClassname}::doValidate(" . $this->getPeerClassname() . "::DATABASE_NAME, " . $this->getPeerClassname() . "::TABLE_NAME, \$columns);
    }
";
    } // end addDoValidate()

    /**
     * Adds the retrieveByPK method for tables with single-column primary key.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addRetrieveByPK_SinglePK(&$script)
    {
        $table = $this->getTable();
        $pks = $table->getPrimaryKey();
        $col = $pks[0];

        $script .= "
    /**
     * Retrieve a single object by pkey.
     *
     * @param " . $col->getPhpType() . " \$pk the primary key.
     * @param      PropelPDO \$con the connection to use
     * @return " . $this->getObjectClassname() . "
     */
    public static function " . $this->getRetrieveMethodName() . "(\$pk, PropelPDO \$con = null)
    {

        if (null !== (\$obj = " . $this->getPeerClassname() . "::getInstanceFromPool(" . $this->getInstancePoolKeySnippet('$pk') . "))) {
            return \$obj;
        }

        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }

        \$criteria = new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME);
        \$criteria->add(" . $this->getColumnConstant($col) . ", \$pk);

        \$v = " . $this->getPeerClassname() . "::doSelect(\$criteria, \$con);

        return !empty(\$v) > 0 ? \$v[0] : null;
    }
";
    }

    /**
     * Adds the retrieveByPKs method for tables with single-column primary key.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addRetrieveByPKs_SinglePK(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Retrieve multiple objects by pkey.
     *
     * @param      array \$pks List of primary keys
     * @param      PropelPDO \$con the connection to use
     * @return "               . $this->getObjectClassname() . "[]
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function " . $this->getRetrieveMethodName() . "s(\$pks, PropelPDO \$con = null)
    {
        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }

        \$objs = null;
        if (empty(\$pks)) {
            \$objs = array();
        } else {
            \$criteria = new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME);";
        $k1 = $table->getPrimaryKey();
        $script .= "
            \$criteria->add(" . $this->getColumnConstant($k1[0]) . ", \$pks, Criteria::IN);";
        $script .= "
            \$objs = " . $this->getPeerClassname() . "::doSelect(\$criteria, \$con);
        }

        return \$objs;
    }
";
    }

    /**
     * Adds the retrieveByPK method for tables with multi-column primary key.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addRetrieveByPK_MultiPK(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Retrieve object using using composite pkey values.";
        foreach ($table->getPrimaryKey() as $col) {
            $clo = strtolower($col->getName());
            $cptype = $col->getPhpType();
            $script .= "
     * @param   $cptype $" . $clo;
        }
        $script .= "
     * @param      PropelPDO \$con
     * @return " . $this->getObjectClassname() . "
     */
    public static function " . $this->getRetrieveMethodName() . "(";

        $php = array();
        foreach ($table->getPrimaryKey() as $col) {
            $clo = strtolower($col->getName());
            $php[] = '$' . $clo;
        } /* foreach */

        $script .= implode(', ', $php);

        $script .= ", PropelPDO \$con = null) {
        \$_instancePoolKey = " . $this->getInstancePoolKeySnippet($php) . ";";
        $script .= "
         if (null !== (\$obj = " . $this->getPeerClassname() . "::getInstanceFromPool(\$_instancePoolKey))) {
             return \$obj;
        }

        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }
        \$criteria = new Criteria(" . $this->getPeerClassname() . "::DATABASE_NAME);";
        foreach ($table->getPrimaryKey() as $col) {
            $clo = strtolower($col->getName());
            $script .= "
        \$criteria->add(" . $this->getColumnConstant($col) . ", $" . $clo . ");";
        }
        $script .= "
        \$v = " . $this->getPeerClassname() . "::doSelect(\$criteria, \$con);

        return !empty(\$v) ? \$v[0] : null;
    }";
    }

    /**
     * Adds the getTableMap() method which is a convenience method for apps to get DB metadata.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addGetTableMap(&$script)
    {
        $script .= "
    /**
     * Returns the TableMap related to this peer.
     * This method is not needed for general use but a specific application could have a need.
     * @return TableMap
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function getTableMap()
    {
        return Propel::getDatabaseMap(" . $this->getPeerClassname() . "::DATABASE_NAME)->getTable(" . $this->getPeerClassname() . "::TABLE_NAME);
    }
";
    }

    /**
     * Adds the complex OM methods to the base addSelectMethods() function.
     *
     * @param string &$script The script will be modified in this method.
     *
     * @see        PeerBuilder::addSelectMethods()
     */
    protected function addSelectMethods(&$script)
    {
        $table = $this->getTable();

        parent::addSelectMethods($script);

        $this->addEnumMethods($script);
        $this->addDoCountJoin($script);
        $this->addDoSelectJoin($script);

        $countFK = count($table->getForeignKeys());

        $includeJoinAll = true;

        foreach ($this->getTable()->getForeignKeys() as $fk) {
            $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());
            $this->declareClassFromBuilder($this->getNewStubPeerBuilder($tblFK));
            if ($tblFK->isForReferenceOnly()) {
                $includeJoinAll = false;
            }
        }

        if ($includeJoinAll) {
            if ($countFK > 0) {
                $this->addDoCountJoinAll($script);
                $this->addDoSelectJoinAll($script);
            }
            if ($countFK > 1) {
                $this->addDoCountJoinAllExcept($script);
                $this->addDoSelectJoinAllExcept($script);
            }
        }
    }

    /**
     * Get the column offsets of the primary key(s) for specified table.
     *
     * @param Table $tbl
     *
     * @return array int[] The column offsets of the primary key(s).
     */
    protected function getPrimaryKeyColOffsets(Table $tbl)
    {
        $offsets = array();
        $idx = 0;
        foreach ($tbl->getColumns() as $col) {
            if ($col->isPrimaryKey()) {
                $offsets[] = $idx;
            }
            $idx++;
        }

        return $offsets;
    }

    public function addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder)
    {
        $script = '';
        $lfMap = $fk->getLocalForeignMapping();
        $lftCols = $fk->getLocalColumns();
        if (count($lftCols) == 1) {
            // simple foreign key
            $lftCol = $lftCols[0];
            $script .= sprintf("
        \$criteria->addJoin(%s, %s, \$join_behavior);
", $this->getColumnConstant($table->getColumn($lftCol)), $joinedTablePeerBuilder->getColumnConstant($joinTable->getColumn($lfMap[$lftCol])));
        } else {
            // composite foreign key
            $script .= "
        \$criteria->addMultipleJoin(array(
";
            foreach ($lftCols as $columnName) {
                $script .= sprintf("        array(%s, %s),
", $this->getColumnConstant($table->getColumn($columnName)), $joinedTablePeerBuilder->getColumnConstant($joinTable->getColumn($lfMap[$columnName])));
            }
            $script .= "      ), \$join_behavior);
";
        }

        return $script;
    }

    /**
     * Adds the doSelectJoin*() methods.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoSelectJoin(&$script)
    {
        $table = $this->getTable();
        $className = $this->getObjectClassname();
        $countFK = count($table->getForeignKeys());
        $join_behavior = $this->getJoinBehavior();

        if ($countFK >= 1) {

            foreach ($table->getForeignKeys() as $fk) {

                $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());

                if (!$joinTable->isForReferenceOnly()) {

                    // This condition is necessary because Propel lacks a system for
                    // aliasing the table if it is the same table.
                    if ($fk->getForeignTableName() != $table->getName()) {

                        $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
                        $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
                        $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);

                        $joinClassName = $joinedTableObjectBuilder->getObjectClassname();

                        $script .= "

    /**
     * Selects a collection of $className objects pre-filled with their $joinClassName objects.
     * @param      Criteria  \$criteria
     * @param      PropelPDO \$con
     * @param      String    \$join_behavior the type of joins to use, defaults to $join_behavior
     * @return array           Array of $className objects.
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function doSelectJoin" . $thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false) . "(Criteria \$criteria, \$con = null, \$join_behavior = $join_behavior)
    {
        \$criteria = clone \$criteria;

        // Set the correct dbName if it has not been overridden
        if (\$criteria->getDbName() == Propel::getDefaultDB()) {
            \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);
        }

        " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        \$startcol = " . $this->getPeerClassname() . "::NUM_HYDRATE_COLUMNS;
        " . $joinedTablePeerBuilder->getPeerClassname() . "::addSelectColumns(\$criteria);
";

                        $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);

                        // apply behaviors
                        $this->applyBehaviorModifier('preSelect', $script);

                        $script .= "
        \$stmt = " . $this->basePeerClassname . "::doSelect(\$criteria, \$con);
        \$results = array();

        while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$key1 = " . $this->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, 0);
            if (null !== (\$obj1 = " . $this->getPeerClassname() . "::getInstanceFromPool(\$key1))) {
                // We no longer rehydrate the object, since this can cause data loss.
                // See http://www.propelorm.org/ticket/509
                // \$obj1->hydrate(\$row, 0, true); // rehydrate
            } else {
";
                        if ($table->getChildrenColumn()) {
                            $script .= "
                \$omClass = " . $this->getPeerClassname() . "::getOMClass(\$row, 0);
                \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
";
                        } else {
                            $script .= "
                \$cls = " . $this->getPeerClassname() . "::getOMClass();
";
                        }
                        $script .= "
                " . $this->buildObjectInstanceCreationCode('$obj1', '$cls') . "
                \$obj1->hydrate(\$row);
                " . $this->getPeerClassname() . "::addInstanceToPool(\$obj1, \$key1);
            } // if \$obj1 already loaded

            \$key2 = " . $joinedTablePeerBuilder->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, \$startcol);
            if (\$key2 !== null) {
                \$obj2 = " . $joinedTablePeerBuilder->getPeerClassname() . "::getInstanceFromPool(\$key2);
                if (!\$obj2) {
";
                        if ($joinTable->getChildrenColumn()) {
                            $script .= "
                    \$omClass = " . $joinedTablePeerBuilder->getPeerClassname() . "::getOMClass(\$row, \$startcol);
                    \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
";
                        } else {
                            $script .= "
                    \$cls = " . $joinedTablePeerBuilder->getPeerClassname() . "::getOMClass();
";
                        }

                        $script .= "
                    " . $this->buildObjectInstanceCreationCode('$obj2', '$cls') . "
                    \$obj2->hydrate(\$row, \$startcol);
                    " . $joinedTablePeerBuilder->getPeerClassname() . "::addInstanceToPool(\$obj2, \$key2);
                } // if obj2 already loaded

                // Add the \$obj1 (" . $this->getObjectClassname() . ") to \$obj2 (" . $joinedTablePeerBuilder->getObjectClassname() . ")";
                        if ($fk->isLocalPrimaryKey()) {
                            $script .= "
                // one to one relationship
                \$obj1->set" . $joinedTablePeerBuilder->getObjectClassname() . "(\$obj2);";
                        } else {
                            $script .= "
                \$obj2->add" . $joinedTableObjectBuilder->getRefFKPhpNameAffix($fk, $plural = false) . "(\$obj1);";
                        }
                        $script .= "

            } // if joined row was not null

            \$results[] = \$obj1;
        }
        \$stmt->closeCursor();

        return \$results;
    }
";
                    } // if fk table name != this table name
                } // if ! is reference only
            } // foreach column
        } // if count(fk) > 1

    } // addDoSelectJoin()

    /**
     * Adds the doCountJoin*() methods.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoCountJoin(&$script)
    {
        $table = $this->getTable();
        $className = $this->getObjectClassname();
        $countFK = count($table->getForeignKeys());
        $join_behavior = $this->getJoinBehavior();

        if ($countFK >= 1) {

            foreach ($table->getForeignKeys() as $fk) {

                $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());

                if (!$joinTable->isForReferenceOnly()) {

                    if ($fk->getForeignTableName() != $table->getName()) {

                        $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
                        $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
                        $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);

                        $joinClassName = $joinedTableObjectBuilder->getObjectClassname();

                        $script .= "

    /**
     * Returns the number of rows matching criteria, joining the related " . $thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false) . " table
     *
     * @param      Criteria \$criteria
     * @param      boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
     * @param      PropelPDO \$con
     * @param      String    \$join_behavior the type of joins to use, defaults to $join_behavior
     * @return int Number of matching rows.
     */
    public static function doCountJoin" . $thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false) . "(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null, \$join_behavior = $join_behavior)
    {
        // we're going to modify criteria, so copy it first
        \$criteria = clone \$criteria;

        // We need to set the primary table name, since in the case that there are no WHERE columns
        // it will be impossible for the BasePeer::createSelectSql() method to determine which
        // tables go into the FROM clause.
        \$criteria->setPrimaryTableName(" . $this->getPeerClassname() . "::TABLE_NAME);

        if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
            \$criteria->setDistinct();
        }

        if (!\$criteria->hasSelectClause()) {
            " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        }

        \$criteria->clearOrderByColumns(); // ORDER BY won't ever affect the count

        // Set the correct dbName
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);

        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }
";
                        $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);

                        // apply behaviors
                        $this->applyBehaviorModifier('preSelect', $script);

                        $script .= "
        \$stmt = " . $this->basePeerClassname . "::doCount(\$criteria, \$con);

        if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$count = (int) \$row[0];
        } else {
            \$count = 0; // no rows returned; we infer that means 0 matches.
        }
        \$stmt->closeCursor();

        return \$count;
    }
";
                    } // if fk table name != this table name
                } // if ! is reference only
            } // foreach column
        } // if count(fk) > 1

    } // addDoCountJoin()

    /**
     * Adds the doSelectJoinAll() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoSelectJoinAll(&$script)
    {
        $table = $this->getTable();
        $className = $this->getObjectClassname();
        $join_behavior = $this->getJoinBehavior();

        $script .= "

    /**
     * Selects a collection of $className objects pre-filled with all related objects.
     *
     * @param      Criteria  \$criteria
     * @param      PropelPDO \$con
     * @param      String    \$join_behavior the type of joins to use, defaults to $join_behavior
     * @return array           Array of $className objects.
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function doSelectJoinAll(Criteria \$criteria, \$con = null, \$join_behavior = $join_behavior)
    {
        \$criteria = clone \$criteria;

        // Set the correct dbName if it has not been overridden
        if (\$criteria->getDbName() == Propel::getDefaultDB()) {
            \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);
        }

        " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        \$startcol2 = " . $this->getPeerClassname() . "::NUM_HYDRATE_COLUMNS;
";
        $index = 2;
        foreach ($table->getForeignKeys() as $fk) {

            // Want to cover this case, but the code is not there yet.
            // Propel lacks a system for aliasing tables of the same name.
            if ($fk->getForeignTableName() != $table->getName()) {
                $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
                $new_index = $index + 1;

                $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
                $joinClassName = $joinedTablePeerBuilder->getObjectClassname();

                $script .= "
        " . $joinedTablePeerBuilder->getPeerClassname() . "::addSelectColumns(\$criteria);
        \$startcol$new_index = \$startcol$index + " . $joinedTablePeerBuilder->getPeerClassname() . "::NUM_HYDRATE_COLUMNS;
";
                $index = $new_index;
            } // if fk->getForeignTableName != table->getName
        } // foreach [sub] foreign keys

        foreach ($table->getForeignKeys() as $fk) {
            // want to cover this case, but the code is not there yet.
            if ($fk->getForeignTableName() != $table->getName()) {
                $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
                $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
                $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);
            }
        }

        // apply behaviors
        $this->applyBehaviorModifier('preSelect', $script);

        $script .= "
        \$stmt = " . $this->basePeerClassname . "::doSelect(\$criteria, \$con);
        \$results = array();

        while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$key1 = " . $this->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, 0);
            if (null !== (\$obj1 = " . $this->getPeerClassname() . "::getInstanceFromPool(\$key1))) {
                // We no longer rehydrate the object, since this can cause data loss.
                // See http://www.propelorm.org/ticket/509
                // \$obj1->hydrate(\$row, 0, true); // rehydrate
            } else {";

        if ($table->getChildrenColumn()) {
            $script .= "
                \$omClass = " . $this->getPeerClassname() . "::getOMClass(\$row, 0);
        \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
";
        } else {
            $script .= "
                \$cls = " . $this->getPeerClassname() . "::getOMClass();
";
        }

        $script .= "
                " . $this->buildObjectInstanceCreationCode('$obj1', '$cls') . "
                \$obj1->hydrate(\$row);
                " . $this->getPeerClassname() . "::addInstanceToPool(\$obj1, \$key1);
            } // if obj1 already loaded
";

        $index = 1;
        foreach ($table->getForeignKeys() as $fk) {
            // want to cover this case, but the code is not there yet.
            // Why not? -because we'd have to alias the tables in the JOIN
            if ($fk->getForeignTableName() != $table->getName()) {
                $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());

                $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
                $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
                $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);


                $joinClassName = $joinedTableObjectBuilder->getObjectClassname();
                $interfaceName = $joinClassName;

                if ($joinTable->getInterface()) {
                    $interfaceName = $this->prefixClassname($joinTable->getInterface());
                }

                $index++;

                $script .= "
            // Add objects for joined $joinClassName rows

            \$key$index = " . $joinedTablePeerBuilder->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, \$startcol$index);
            if (\$key$index !== null) {
                \$obj$index = " . $joinedTablePeerBuilder->getPeerClassname() . "::getInstanceFromPool(\$key$index);
                if (!\$obj$index) {
";
                if ($joinTable->getChildrenColumn()) {
                    $script .= "
                    \$omClass = " . $joinedTablePeerBuilder->getPeerClassname() . "::getOMClass(\$row, \$startcol$index);
          \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
";
                } else {
                    $script .= "
                    \$cls = " . $joinedTablePeerBuilder->getPeerClassname() . "::getOMClass();
";
                } /* $joinTable->getChildrenColumn() */

                $script .= "
                    " . $this->buildObjectInstanceCreationCode('$obj' . $index, '$cls') . "
                    \$obj" . $index . "->hydrate(\$row, \$startcol$index);
                    " . $joinedTablePeerBuilder->getPeerClassname() . "::addInstanceToPool(\$obj$index, \$key$index);
                } // if obj$index loaded

                // Add the \$obj1 (" . $this->getObjectClassname() . ") to the collection in \$obj" . $index . " (" . $joinedTablePeerBuilder->getObjectClassname() . ")";
                if ($fk->isLocalPrimaryKey()) {
                    $script .= "
                \$obj1->set" . $joinedTablePeerBuilder->getObjectClassname() . "(\$obj" . $index . ");";
                } else {
                    $script .= "
                \$obj" . $index . "->add" . $joinedTableObjectBuilder->getRefFKPhpNameAffix($fk, $plural = false) . "(\$obj1);";
                }
                $script .= "
            } // if joined row not null
";
            } // $fk->getForeignTableName() != $table->getName()
        } //foreach foreign key

        $script .= "
            \$results[] = \$obj1;
        }
        \$stmt->closeCursor();

        return \$results;
    }
";
    } // end addDoSelectJoinAll()


    /**
     * Adds the doCountJoinAll() method.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoCountJoinAll(&$script)
    {
        $table = $this->getTable();
        $className = $this->getObjectClassname();
        $join_behavior = $this->getJoinBehavior();

        $script .= "

    /**
     * Returns the number of rows matching criteria, joining all related tables
     *
     * @param      Criteria \$criteria
     * @param      boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
     * @param      PropelPDO \$con
     * @param      String    \$join_behavior the type of joins to use, defaults to $join_behavior
     * @return int Number of matching rows.
     */
    public static function doCountJoinAll(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null, \$join_behavior = $join_behavior)
    {
        // we're going to modify criteria, so copy it first
        \$criteria = clone \$criteria;

        // We need to set the primary table name, since in the case that there are no WHERE columns
        // it will be impossible for the BasePeer::createSelectSql() method to determine which
        // tables go into the FROM clause.
        \$criteria->setPrimaryTableName(" . $this->getPeerClassname() . "::TABLE_NAME);

        if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
            \$criteria->setDistinct();
        }

        if (!\$criteria->hasSelectClause()) {
            " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        }

        \$criteria->clearOrderByColumns(); // ORDER BY won't ever affect the count

        // Set the correct dbName
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);

        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }
";

        foreach ($table->getForeignKeys() as $fk) {
            // want to cover this case, but the code is not there yet.
            if ($fk->getForeignTableName() != $table->getName()) {
                $joinTable = $table->getDatabase()->getTable($fk->getForeignTableName());
                $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
                $script .= $this->addCriteriaJoin($fk, $table, $joinTable, $joinedTablePeerBuilder);
            } // if fk->getForeignTableName != table->getName
        } // foreach [sub] foreign keys

        // apply behaviors
        $this->applyBehaviorModifier('preSelect', $script);

        $script .= "
        \$stmt = " . $this->basePeerClassname . "::doCount(\$criteria, \$con);

        if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$count = (int) \$row[0];
        } else {
            \$count = 0; // no rows returned; we infer that means 0 matches.
        }
        \$stmt->closeCursor();

        return \$count;
    }";
    } // end addDoCountJoinAll()

    /**
     * Adds the doSelectJoinAllExcept*() methods.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoSelectJoinAllExcept(&$script)
    {
        $table = $this->getTable();
        $join_behavior = $this->getJoinBehavior();

        // ------------------------------------------------------------------------
        // doSelectJoinAllExcept*()
        // ------------------------------------------------------------------------

        // 2) create a bunch of doSelectJoinAllExcept*() methods
        // -- these were existing in original Torque, so we should keep them for compatibility

        $fkeys = $table->getForeignKeys(); // this sep assignment is necessary otherwise sub-loops over
        // getForeignKeys() will cause this to only execute one time.
        foreach ($fkeys as $fk) {

            $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());

            $excludedTable = $table->getDatabase()->getTable($fk->getForeignTableName());

            $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
            $excludedTableObjectBuilder = $this->getNewObjectBuilder($excludedTable);
            $excludedTablePeerBuilder = $this->getNewPeerBuilder($excludedTable);

            $excludedClassName = $excludedTableObjectBuilder->getObjectClassname();


            $script .= "

    /**
     * Selects a collection of " . $this->getObjectClassname() . " objects pre-filled with all related objects except " . $thisTableObjectBuilder->getFKPhpNameAffix($fk) . ".
     *
     * @param      Criteria  \$criteria
     * @param      PropelPDO \$con
     * @param      String    \$join_behavior the type of joins to use, defaults to $join_behavior
     * @return array           Array of " . $this->getObjectClassname() . " objects.
     * @throws PropelException Any exceptions caught during processing will be
     *		 rethrown wrapped into a PropelException.
     */
    public static function doSelectJoinAllExcept" . $thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false) . "(Criteria \$criteria, \$con = null, \$join_behavior = $join_behavior)
    {
        \$criteria = clone \$criteria;

        // Set the correct dbName if it has not been overridden
        // \$criteria->getDbName() will return the same object if not set to another value
        // so == check is okay and faster
        if (\$criteria->getDbName() == Propel::getDefaultDB()) {
            \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);
        }

        " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        \$startcol2 = " . $this->getPeerClassname() . "::NUM_HYDRATE_COLUMNS;
";
            $index = 2;
            foreach ($table->getForeignKeys() as $subfk) {
                // want to cover this case, but the code is not there yet.
                // Why not? - because we would have to alias the tables in the join
                if (!($subfk->getForeignTableName() == $table->getName())) {
                    $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());
                    $joinTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
                    $joinClassName = $joinTablePeerBuilder->getObjectClassname();

                    if ($joinClassName != $excludedClassName) {
                        $new_index = $index + 1;
                        $script .= "
        " . $joinTablePeerBuilder->getPeerClassname() . "::addSelectColumns(\$criteria);
        \$startcol$new_index = \$startcol$index + " . $joinTablePeerBuilder->getPeerClassname() . "::NUM_HYDRATE_COLUMNS;
";
                        $index = $new_index;
                    } // if joinClassName not excludeClassName
                } // if subfk is not curr table
            } // foreach [sub] foreign keys

            foreach ($table->getForeignKeys() as $subfk) {
                // want to cover this case, but the code is not there yet.
                if ($subfk->getForeignTableName() != $table->getName()) {
                    $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());
                    $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
                    $joinClassName = $joinedTablePeerBuilder->getObjectClassname();

                    if ($joinClassName != $excludedClassName) {
                        $script .= $this->addCriteriaJoin($subfk, $table, $joinTable, $joinedTablePeerBuilder);
                    }
                }
            } // foreach fkeys

            // apply behaviors
            $this->applyBehaviorModifier('preSelect', $script);

            $script .= "

        \$stmt = " . $this->basePeerClassname . "::doSelect(\$criteria, \$con);
        \$results = array();

        while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$key1 = " . $this->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, 0);
            if (null !== (\$obj1 = " . $this->getPeerClassname() . "::getInstanceFromPool(\$key1))) {
                // We no longer rehydrate the object, since this can cause data loss.
                // See http://www.propelorm.org/ticket/509
                // \$obj1->hydrate(\$row, 0, true); // rehydrate
            } else {";
            if ($table->getChildrenColumn()) {
                $script .= "
                \$omClass = " . $this->getPeerClassname() . "::getOMClass(\$row, 0);
                \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
";
            } else {
                $script .= "
                \$cls = " . $this->getPeerClassname() . "::getOMClass();
";
            }

            $script .= "
                " . $this->buildObjectInstanceCreationCode('$obj1', '$cls') . "
                \$obj1->hydrate(\$row);
                " . $this->getPeerClassname() . "::addInstanceToPool(\$obj1, \$key1);
            } // if obj1 already loaded
";

            $index = 1;
            foreach ($table->getForeignKeys() as $subfk) {
                // want to cover this case, but the code is not there yet.
                if ($subfk->getForeignTableName() != $table->getName()) {

                    $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());

                    $joinedTableObjectBuilder = $this->getNewObjectBuilder($joinTable);
                    $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);

                    $joinClassName = $joinedTableObjectBuilder->getObjectClassname();

                    $interfaceName = $joinClassName;
                    if ($joinTable->getInterface()) {
                        $interfaceName = $this->prefixClassname($joinTable->getInterface());
                    }

                    if ($joinClassName != $excludedClassName) {

                        $index++;

                        $script .= "
                // Add objects for joined $joinClassName rows

                \$key$index = " . $joinedTablePeerBuilder->getPeerClassname() . "::getPrimaryKeyHashFromRow(\$row, \$startcol$index);
                if (\$key$index !== null) {
                    \$obj$index = " . $joinedTablePeerBuilder->getPeerClassname() . "::getInstanceFromPool(\$key$index);
                    if (!\$obj$index) {
    ";

                        if ($joinTable->getChildrenColumn()) {
                            $script .= "
                        \$omClass = " . $joinedTablePeerBuilder->getPeerClassname() . "::getOMClass(\$row, \$startcol$index);
            \$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);
";
                        } else {
                            $script .= "
                        \$cls = " . $joinedTablePeerBuilder->getPeerClassname() . "::getOMClass();
";
                        } /* $joinTable->getChildrenColumn() */
                        $script .= "
                    " . $this->buildObjectInstanceCreationCode('$obj' . $index, '$cls') . "
                    \$obj" . $index . "->hydrate(\$row, \$startcol$index);
                    " . $joinedTablePeerBuilder->getPeerClassname() . "::addInstanceToPool(\$obj$index, \$key$index);
                } // if \$obj$index already loaded

                // Add the \$obj1 (" . $this->getObjectClassname() . ") to the collection in \$obj" . $index . " (" . $joinedTablePeerBuilder->getObjectClassname() . ")";
                        if ($subfk->isLocalPrimaryKey()) {
                            $script .= "
                \$obj1->set" . $joinedTablePeerBuilder->getObjectClassname() . "(\$obj" . $index . ");";
                        } else {
                            $script .= "
                \$obj" . $index . "->add" . $joinedTableObjectBuilder->getRefFKPhpNameAffix($subfk, $plural = false) . "(\$obj1);";
                        }
                        $script .= "

            } // if joined row is not null
";
                    } // if ($joinClassName != $excludedClassName) {
                } // $subfk->getForeignTableName() != $table->getName()
            } // foreach
            $script .= "
            \$results[] = \$obj1;
        }
        \$stmt->closeCursor();

        return \$results;
    }
";
        } // foreach fk

    } // addDoSelectJoinAllExcept

    /**
     * Adds the doCountJoinAllExcept*() methods.
     *
     * @param string &$script The script will be modified in this method.
     */
    protected function addDoCountJoinAllExcept(&$script)
    {
        $table = $this->getTable();
        $join_behavior = $this->getJoinBehavior();

        $fkeys = $table->getForeignKeys(); // this sep assignment is necessary otherwise sub-loops over
        // getForeignKeys() will cause this to only execute one time.
        foreach ($fkeys as $fk) {

            $tblFK = $table->getDatabase()->getTable($fk->getForeignTableName());

            $excludedTable = $table->getDatabase()->getTable($fk->getForeignTableName());

            $thisTableObjectBuilder = $this->getNewObjectBuilder($table);
            $excludedTableObjectBuilder = $this->getNewObjectBuilder($excludedTable);
            $excludedTablePeerBuilder = $this->getNewPeerBuilder($excludedTable);

            $excludedClassName = $excludedTableObjectBuilder->getObjectClassname();

            $script .= "

    /**
     * Returns the number of rows matching criteria, joining the related " . $thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false) . " table
     *
     * @param      Criteria \$criteria
     * @param      boolean \$distinct Whether to select only distinct columns; deprecated: use Criteria->setDistinct() instead.
     * @param      PropelPDO \$con
     * @param      String    \$join_behavior the type of joins to use, defaults to $join_behavior
     * @return int Number of matching rows.
     */
    public static function doCountJoinAllExcept" . $thisTableObjectBuilder->getFKPhpNameAffix($fk, $plural = false) . "(Criteria \$criteria, \$distinct = false, PropelPDO \$con = null, \$join_behavior = $join_behavior)
    {
        // we're going to modify criteria, so copy it first
        \$criteria = clone \$criteria;

        // We need to set the primary table name, since in the case that there are no WHERE columns
        // it will be impossible for the BasePeer::createSelectSql() method to determine which
        // tables go into the FROM clause.
        \$criteria->setPrimaryTableName(" . $this->getPeerClassname() . "::TABLE_NAME);

        if (\$distinct && !in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
            \$criteria->setDistinct();
        }

        if (!\$criteria->hasSelectClause()) {
            " . $this->getPeerClassname() . "::addSelectColumns(\$criteria);
        }

        \$criteria->clearOrderByColumns(); // ORDER BY should not affect count

        // Set the correct dbName
        \$criteria->setDbName(" . $this->getPeerClassname() . "::DATABASE_NAME);

        if (\$con === null) {
            \$con = Propel::getConnection(" . $this->getPeerClassname() . "::DATABASE_NAME, Propel::CONNECTION_READ);
        }
    ";

            foreach ($table->getForeignKeys() as $subfk) {
                // want to cover this case, but the code is not there yet.
                if ($subfk->getForeignTableName() != $table->getName()) {
                    $joinTable = $table->getDatabase()->getTable($subfk->getForeignTableName());
                    $joinedTablePeerBuilder = $this->getNewPeerBuilder($joinTable);
                    $joinClassName = $joinedTablePeerBuilder->getObjectClassname();

                    if ($joinClassName != $excludedClassName) {
                        $script .= $this->addCriteriaJoin($subfk, $table, $joinTable, $joinedTablePeerBuilder);
                    }
                }
            } // foreach fkeys

            // apply behaviors
            $this->applyBehaviorModifier('preSelect', $script);

            $script .= "
        \$stmt = " . $this->basePeerClassname . "::doCount(\$criteria, \$con);

        if (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
            \$count = (int) \$row[0];
        } else {
            \$count = 0; // no rows returned; we infer that means 0 matches.
        }
        \$stmt->closeCursor();

        return \$count;
    }
";
        } // foreach fk

    } // addDoCountJoinAllExcept

    /**
     * returns the desired join behavior as set in the build properties
     * see trac ticket #588, #491
     *
     */
    protected function getJoinBehavior()
    {
        return $this->getGeneratorConfig()->getBuildProperty('useLeftJoinsInDoJoinMethods') ? 'Criteria::LEFT_JOIN' : 'Criteria::INNER_JOIN';
    }
} // PHP5PeerBuilder
